package xyz.jcat.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import xyz.jcat.common.util.ArrayUtils;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {

    public static RedisTemplate redisTemplate;
    public static StringRedisTemplate stringRedisTemplate;
    //不设置超时时间
    public static final long DEFAULT_TIMEOUT = -1;
    public static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;

    public static void set(IRedisKey redisKey, Object value, Object... uids) {
        set(getKey(redisKey, uids), value);
    }
    public static void set(String key, Object value) {
        set(key, value, DEFAULT_TIMEOUT);
    }
    public static void set(String key, Object value, long timeout) {
        set(key, value, timeout, DEFAULT_TIME_UNIT, false);
    }

    public static void set(String key, Object value, long timeout, TimeUnit timeUnit) {
        set(key, value, timeout, timeUnit, false);
    }

    public static void set(String key, Object value, long timeout, TimeUnit timeUnit, boolean allowedNull) {
        if(Objects.isNull(value) && !allowedNull) {
            return;
        }
        RedisTemplate template = redisTemplate;
        if(value instanceof String) {
            template = stringRedisTemplate;
        }
        if(timeout > 0) {
            template.opsForValue().set(key, value, timeout, timeUnit);
        }else {
            template.opsForValue().set(key, value);
        }
    }

    public static Optional<String> get(IRedisKey redisKey, Object... uids) {
        return get(getKey(redisKey, uids));
    }

    public static <V> Optional<V> get(IRedisKey redisKey, Class<V> clazz, Object... uids) {
        return get(getKey(redisKey, uids), clazz);
    }

    public static Optional<String> get(String key) {
        return Optional.ofNullable(stringRedisTemplate.opsForValue().get(key));
    }

    public static <V> Optional<V> get(String key, Class<V> clazz) {
        return Optional.ofNullable((V) redisTemplate.opsForValue().get(key));
    }

    public static <V> Optional<List<V>> getList(String key, Class<V> clazz) {
        Object obj = redisTemplate.opsForValue().get(key);
        if(Objects.isNull(obj)){
            return Optional.empty();
        }
        return Optional.of((List<V>) obj);
    }

    public static <V> Optional<List<V>> getList(IRedisKey redisKey, Class<V> clazz, Object... uids) {
        return getList(getKey(redisKey, uids), clazz);
    }

    public static boolean del(String key) {
        return redisTemplate.delete(key);
    }

    public static boolean del(IRedisKey redisKey, Object... uids) {
        return del(getKey(redisKey, uids));
    }

    public static boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    public static boolean exists(IRedisKey redisKey, Object... uids) {
        return exists(getKey(redisKey, uids));
    }

    public static void expire(String key, long timeout, TimeUnit timeUnit) {
        redisTemplate.expire(key, timeout, timeUnit);
    }

    public static void hashPut(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public static void hashPut(IRedisKey redisKey, String hashKey, Object value, Object... uids) {
        hashPut(getKey(redisKey, uids), hashKey, value);
    }

    public static <V> Optional<V> hashGet(IRedisKey redisKey, String hashKey, Class<V> clazz, Object... uids) {
        return hashGet(getKey(redisKey, uids), hashKey, clazz);
    }

    public static <V> Optional<List<V>> hashGetList(IRedisKey redisKey, String hashKey, Class<V> clazz, Object... uids) {
        return hashGetList(getKey(redisKey, uids), hashKey, clazz);
    }

    public static <V> Optional<V> hashGet(String key, String hashKey, Class<V> clazz) {
       return Optional.ofNullable((V) redisTemplate.opsForHash().get(key, hashKey));
    }

    public static <V> Optional<List<V>> hashGetList(String key, String hashKey, Class<V> clazz) {
        Object obj = redisTemplate.opsForHash().get(key, hashKey);
        if(Objects.isNull(obj)){
            return Optional.empty();
        }
        return Optional.of((List<V>) obj);
    }

    public static long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Autowired
    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public static String getKey(IRedisKey redisKey, Object... uids) {
        return getKey(redisKey.getPrefix(), uids);
    }

    public static String getKey(String prefix, Object... uids) {
        if(ArrayUtils.isEmpty(uids)) {
            return prefix;
        }
        uids = Arrays.stream(uids).map(Object::toString).toArray();
        return MessageFormat.format(prefix, uids);
    }
}
